package mplugin

import (
	"context"
	"fmt"
	"gitee.com/ChanYeung/go-plugin/util"
	"path"
	"plugin"
	"strings"
)

type PluginFunc func(ctx context.Context) error

// PluginItem 存储着插件的信息

type PluginItem struct {
	PluginPath string
	PBaseInfo  *PluginBaseInfo
	plugin     *plugin.Plugin
}
type PluginBaseInfo struct {
	Name        string
	ActiveFlag  string //目标任务执行前运行
	ActivePoint bool
	Functions   string //可用函数
}

// 所有插件必须实现该方法

// loadAllPlugin 将会过滤一次传入的targetFile,同时将so后缀的文件装载，并返回一个插件信息集合
var loadedPlugins []PluginItem

// PluginSearch 从plugin baseinfo中提取 当前插件是否包含目标函数
func PluginSearch(funcName PluginType) []PluginItem {
	var plugins []PluginItem
	for i := range loadedPlugins {
		if strings.Contains(loadedPlugins[i].PBaseInfo.Functions, string(funcName)) {
			plugins = append(plugins, loadedPlugins[i])
		}
	}
	return plugins
}
func loadAllPlugin(targetFile []string) {

	for _, fileItem := range targetFile {
		// 过滤插件文件
		if path.Ext(fileItem) == ".so" {
			pluginFile, err := plugin.Open(fileItem)
			if err != nil {
				fmt.Println("LoadAllPlugin has an error occurred : [" + fileItem + "], drop it")
				fmt.Println(err)
				continue
			}
			//查找指定函数或符号
			targetFunc, err := pluginFile.Lookup(PluginType_PluginInfo)
			if err != nil {
				fmt.Println("An error occurred while search  [" + PluginType_PluginInfo + "] from [" + fileItem + "], not support plugin, please add var PluginInfo")
				continue
			}
			baseInfo, ok := targetFunc.(*PluginBaseInfo)
			if !ok {
				fmt.Println("An error occurred while search  [" + PluginType_PluginInfo + "] from [" + fileItem + "], not support plugin, please add var PluginInfo")
				continue
			}
			//采集插件信息
			pluginInfo := PluginItem{
				PluginPath: fileItem,
				PBaseInfo:  baseInfo,
				plugin:     pluginFile,
			}
			fmt.Printf("Loaded plugin [%v]\n", pluginInfo.PluginPath)
			loadedPlugins = append(loadedPlugins, pluginInfo)
		}
	}
}

func InitPlugins() {
	// 查找插件文件 加载
	pluginFiles := util.FindFile("plugins")
	loadAllPlugin(pluginFiles)
}

func DoInvokePlugin(pType PluginType) {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println("DoInvokePlugin has an err:", err)
		}
	}()
	// 执行插件调用
	search := PluginSearch(pType)
	for _, pg := range search {
		symbol, err := pg.plugin.Lookup(string(pType))
		if err != nil {
			fmt.Printf("An error occurred while search target func [%v] from plugin[%v] ||msg=%v\n", pType, pg.PluginPath, err.Error())
			continue
		}
		if f, ok := symbol.(func(ctx context.Context) error); !ok {
			fmt.Printf("Hit plugin[%v], but covert plugin type err||msg=%v\n", pType, err.Error())
		} else {
			if err := f(context.Background()); err != nil {
				fmt.Printf("Hit plugin[%v], but call plugin err||msg=%v\n", pType, err.Error())
			}
		}
	}
}
